import base64
import pickle
import json
import logging
from django import http
from django.shortcuts import render
from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin
from django_redis import get_redis_connection

from meiduo_mall.utils.views import LoginRequiredJSONMixin
from meiduo_mall.utils.response_code import RETCODE
from goods.models import SKU
# Create your views here.

logger = logging.getLogger('django')


class CartsView(View):
    """购物车视图"""
    def post(self, request):
        """添加购物车"""
        # 接收和校验参数
        request_data = json.loads(request.body.decode())
        sku_id = request_data.get('sku_id', None)
        count = request_data.get('count', None)
        selected = request_data.get('selected', True)

        if not all([sku_id, count]):
            return http.HttpResponseForbidden('缺少比传参数')

        # 判断是否存在该商品
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('不存在该商品')

        # 判断selected的类型
        if not isinstance(selected, bool):
            return http.HttpResponseForbidden('参数有误')

        # 判断用户是否登录
        if request.user.is_authenticated():
            # 登录，将数据写入ｒｅｄｉｓ中
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            pl.hincrby('carts_{}'.format(request.user.id), sku_id, count)
            if selected:
                pl.sadd('selected_{}'.format(request.user.id), sku_id)

            pl.execute()

            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
        else:  # 未登录，将数据写入ｃｏｏｋｉｅ中
            """
            {
                sku_id: {
                    count:,
                    selected:,
                },
                ...
            }
            """
            carts_cookie = request.COOKIES.get('carts', None)
            if carts_cookie:  # 有记录
                carts_cookie = pickle.loads(base64.b64decode(carts_cookie.encode()))
                if sku_id in carts_cookie:  # 购物车中之前有该商品
                    carts_cookie[sku_id]['count'] += count
                    carts_cookie[sku_id]['selected'] = selected
                else:  # 购物车中没有该商品
                    carts_cookie[sku_id] = {'count': count, 'selected': selected}

                carts_cookie = base64.b64encode(pickle.dumps(carts_cookie)).decode()
            else:  # 无记录
                carts_cookie = base64.b64encode(pickle.dumps({sku_id: {'count': count, 'selected': selected}})).decode()

            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
            response.set_cookie('carts', carts_cookie)

            return response

    def get(self, request):
        """展示购物车"""
        # 判断用户是否登录
        if request.user.is_authenticated():  # # 已登录用redis中的数据
            redis_conn = get_redis_connection('carts')
            cart_data = redis_conn.hgetall('carts_{}'.format(request.user.id))
            selected_skus = [cur_sku.decode() for cur_sku in redis_conn.smembers('selected_{}'.format(request.user.id))]
            carts = []
            # skus = SKU.objects.filter(id__in=[sku_id.decode() for sku_id in cart_data.keys()])
            for sku_id, count in cart_data.items():
                try:
                    sku_id = sku_id.decode()
                    sku = SKU.objects.get(id=sku_id)
                    carts.append({
                        'id': sku_id,
                        'selected': str(True) if sku_id in selected_skus else str(False),  # 后端的bool值传到前端，要将bool转str，方便那前端解析
                        'default_image_url': sku.default_image.url,
                        'name': sku.name,
                        'price': str(sku.price),  # 价格要转为str传到前端
                        'count': count.decode(),
                        'amount': str(sku.price * int(count.decode()))
                    })
                except Exception as e:
                    logger.error(e)
                    return http.render(request, '404.html')
        else:  # 未登录用cookie中的数据
            cart_cookie = request.COOKIES.get('carts', None)
            if not cart_cookie:
                return render(request, '404.html')
            cart = pickle.loads(base64.b64decode(cart_cookie.encode()))
            carts = []
            for sku_id, detail in cart.items():
                try:
                    sku = SKU.objects.get(id=sku_id)
                    carts.append({
                        'id': sku_id,
                        'selected': str(detail['selected']),
                        'default_image_url': sku.default_image.url,
                        'name': sku.name,
                        'price': str(sku.price),
                        'count': detail['count'],
                        'amount': str(sku.price * int(detail['count']))
                    })
                except Exception as e:
                    logger.error(e)
                    return render(request, '404.html')

        # 构造上下文
        context = {
            'carts': carts
        }
        # 返回结果
        return render(request, 'cart.html', context=context)

    def put(self, request):
        """修改购物车"""
        # 接收校验参数
        request_data = json.loads(request.body.decode())
        sku_id = request_data.get('sku_id', None)
        count = int(request_data.get('count', -1))
        selected = request_data.get('selected', None)
        if not all([sku_id, count, str(selected)]):
            return http.HttpResponseForbidden('缺少参数')
        if not isinstance(selected, bool):
            return http.HttpResponseForbidden('参数有误')

        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('不存在的商品')

        # 判断用户是否登录
        user = request.user
        if user.is_authenticated():  # 用户登录逻辑
            # 读取redis信息并修改
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            pl.hmset('carts_{}'.format(user.id), {sku_id: count})
            if selected:
                pl.sadd('selected_{}'.format(user.id), sku_id)
            else:
                pl.srem('selected_{}'.format(user.id), sku_id)
            pl.execute()

            # 构造返回结果
            carts_sku = {
                'id': sku_id,
                'selected': selected,
                'default_image_url': sku.default_image.url,
                'name': sku.name,
                'price': str(sku.price),
                'count': str(count),
                'amount': str(sku.price * int(count))
            }

            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'cart_sku': carts_sku})
        else:  # 未登录逻辑
            # 读取并修改cookie信息
            cart_cookies = request.COOKIES.get('carts', None)
            cart_cookies = cart_cookies.encode()
            cart_cookies = base64.b64decode(cart_cookies)
            cart_cookies = pickle.loads(cart_cookies)
            # cart_cookies = pickle.loads(base64.b64decode(cart_cookies.encode()))
            cart_cookies[sku_id]['count'] = count
            cart_cookies[sku_id]['selected'] = selected

            # 构造返回结果
            carts_sku = {
                'id': sku_id,
                'selected': selected,
                'default_image_url': sku.default_image.url,
                'name': sku.name,
                'price': str(sku.price),
                'count': str(count),
                'amount': str(sku.price * int(count))
            }
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'cart_sku': carts_sku})
            response.set_cookie('carts', base64.b64encode(pickle.dumps(cart_cookies)).decode())
            return response

    def delete(self, request):
        """删除购物车中的商品"""
        # 接收和校验参数
        json_data = json.loads(request.body.decode())
        sku_id = json_data.get('sku_id')
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('不能存在的商品')

        # 判断用户是否登录
        if request.user.is_authenticated():  # 登录用户删除购物车逻辑
            # 从缓存中删除数据
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            pl.hdel('carts_{}'.format(request.user.id), sku_id)
            pl.srem('selected_{}'.format(request.user.id), sku_id)
            pl.execute()
            # 返回响应
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
        else:  # 未登录用户删除购物车逻辑
            # 获取cookie信息
            cart_cookie = request.COOKIES.get('carts')
            cart_cookie = pickle.loads(base64.b64decode(cart_cookie.encode()))

            # 构造响应对象
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})

            # 删除cookie中的数据
            if sku_id in cart_cookie:  # pop和del，如果字典中不存在该数据，使用的话会抛异常
                cart_cookie.pop(sku_id)
                # 只有当cookie别修改的时候才重新写入cookie，否则不用再重复写cookie，提高代码执行效率
                if not cart_cookie:  # 清空了购物车
                    response.delete_cookie('carts')
                else:  # 购物车中还存在其他商品
                    # 重新构造cookie
                    cart_cookie = base64.b64encode(pickle.dumps(cart_cookie)).decode()
                    response.set_cookie('carts', cart_cookie)

            return response


class SelectedAllView(View):
    """全选视图"""
    def put(self, request):
        # 接收和校验参数
        json_dict = json.loads(request.body.decode())
        selected = json_dict.get('selected', None)
        if not isinstance(selected, bool):
            return http.HttpResponseForbidden('参数错误')
        # 判断用户是否登录
        if request.user.is_authenticated():  # 登录用户逻辑
            redis_conn = get_redis_connection('carts')
            sku_id_list = redis_conn.hkeys('carts_{}'.format(request.user.id))
            if selected:  # 全选
                for sku_id in sku_id_list:
                    # api支持解包操作,即redis_conn.sadd(key, *value)  # value 是列表
                    redis_conn.sadd('selected_{}'.format(request.user.id), sku_id.decode())
            else:  # 全不选
                for sku_id in sku_id_list:
                    redis_conn.srem('selected_{}'.format(request.user.id), sku_id.decode())

            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
        else:  # 未登录用户逻辑
            cart_cookies = pickle.loads(
                base64.b64decode(
                    request.COOKIES.get('carts', None).encode()
                )
            )
            new_cart_cookies = {k: {'count': v['count'], 'selected': selected} for k, v in cart_cookies.items()}
            set_cookies = base64.b64encode(pickle.dumps(new_cart_cookies)).decode()
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
            response.set_cookie('carts', set_cookies)
            return response


class CartSimpleView(View):
    """展示购物车简易信息视图"""
    def get(self, request):
        if request.user.is_authenticated():  # 登录用户
            # 从redis中读取购物车信息
            redis_conn = get_redis_connection('carts')
            carts = redis_conn.hgetall('carts_{}'.format(request.user.id))
            # 构造响应结果
            try:
                cart_skus = [
                    {
                        'id': sku_id.decode(),
                        'default_image_url': SKU.objects.get(id=sku_id.decode()).default_image.url,
                        'name': SKU.objects.get(id=sku_id.decode()).name,
                        'count': count.decode()
                    }
                    for sku_id, count in carts.items()
                ]
            except Exception as e:
                logger.error(e)
                return http.HttpResponseServerError('加载数据失败')
        else:  # 未登录用
            cart_cookie = request.COOKIES.get('carts', None)
            if not cart_cookie:
                return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
            carts = pickle.loads(base64.b64decode(cart_cookie.encode()))
            try:
                cart_skus = [
                    {
                        'id': sku_id,
                        'default_image_url': SKU.objects.get(id=sku_id).default_image.url,
                        'name': SKU.objects.get(id=sku_id).name,
                        'count': value['count']
                    }
                    for sku_id, value in carts.items()
                ]
            except Exception as e:
                logger.error(e)
                return http.HttpResponseServerError('数据加载失败')

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'cart_skus': cart_skus})


